/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/variable/Module_interface.h"
#include "simodo/variable/VariableSetWrapper.h"

#include "simodo/interpret/Interpret_interface.h"
#include "simodo/loom/Loom_interface.h"
#include "simodo/loom/FiberStatus.h"

#include "simodo/inout/convert/functions.h"

// #include <iostream>
#include <memory>
#include <cassert>

#ifdef CROSS_WIN
// MinGW related workaround
#define BOOST_DLL_FORCE_ALIAS_INSTANTIATION
#endif

#include <boost/dll/alias.hpp>

using namespace simodo;
using namespace simodo::variable;

namespace
{
    Value get_test_string(Module_interface * host, const VariableSetWrapper & );
    Value set_test_string(Module_interface * host, const VariableSetWrapper & args);
    Value get_fibers(Module_interface * host, const VariableSetWrapper & );
}

class ModuleHost_test : public Module_interface
{
    std::u16string                   _test_string; // = u"initial test string";
    interpret::Interpret_interface * _interpret = nullptr;

public:
    ModuleHost_test(interpret::Interpret_interface * interpret)
    {
        _test_string = u"initial test string";
        _interpret = interpret;
    }

    const std::u16string & test_string() const { return _test_string; }
    void setTestString(const std::u16string & str) { _test_string = str; }

    interpret::Interpret_interface * interpret() { return _interpret; }

    virtual version_t version() const override { return lib_version(); }

    virtual Object instantiate(std::shared_ptr<variable::Module_interface> module_object) override 
    {
        // cout << "getNamespace(0x" << hex << module_host << ")"<< endl;
        // cout << "." << convertToU8(_test_string) << endl;

        return {{
            {u"doc", u"Нужно будет предусмотреть поддержку трансляции"},
            // {u"doc", u"\\href http://.../README.md", {}, {}},
            // {u"pi", 3.14, {}, {{u"doc", u"Число pi", {}, {}}}},
            {u"get_test_string", {ValueType::Function, Object {{
                {u"@", ExternalFunction {module_object, get_test_string}},
                {{}, ValueType::String},
            }}}},
            {u"set_test_string", {ValueType::Function, Object {{
                {u"@", ExternalFunction {module_object, set_test_string}},
                {{}, ValueType::Null},
                {u"test_string", ValueType::String},
            }}}},
            {u"get_fibers", {ValueType::Function, Object {{
                {u"@", ExternalFunction {module_object, get_fibers}},
                {{}, ValueType::Object},
            }}}},
        }};
    }

    // Factory method
    static std::shared_ptr<Module_interface> create(interpret::Interpret_interface * interpret) {
        return std::make_shared<ModuleHost_test>(interpret);
    }
};

BOOST_DLL_ALIAS(
    ModuleHost_test::create,    // <-- this function is exported with...
    create_simodo_module        // <-- ...this alias name
)

namespace
{
    Value get_test_string(Module_interface * host, const VariableSetWrapper & )
    {
        // cout << "get_test_string(0x" << hex << host << ",)"<< endl;
        assert(host != nullptr);
        ModuleHost_test * test = static_cast<ModuleHost_test *>(host);
        // cout << "." << convertToU8(test->test_string()) << endl;

        return test->test_string();
    }

    Value set_test_string(Module_interface * host, const VariableSetWrapper & args)
    {
        // cout << "set_test_string(0x" << hex << host << ",)"<< endl;
        assert(host != nullptr);
        ModuleHost_test * test = static_cast<ModuleHost_test *>(host);
        // cout << "." << convertToU8(test->test_string()) << endl;

        assert(args.size() == 1);

        test->setTestString(args[0].origin().value().getString());
        // test->setTestString(u"set_test_string called");

        return {};
    }

    Value get_fibers(Module_interface * host, const VariableSetWrapper & )
    {
        assert(host != nullptr);
        ModuleHost_test * test = static_cast<ModuleHost_test *>(host);

        if (test->interpret() == nullptr)
            return {u"Have to use script module for fibers request"};

        const loom::Loom_interface * loom = test->interpret()->loom();
        assert(loom);

        variable::VariableSet_t fibers_info;

        std::vector<loom::FiberStructure> fibers = loom->fibers();

        for(const loom::FiberStructure & f : fibers)
            fibers_info.push_back({inout::toU16(std::to_string(int64_t(f.no))), inout::toU16(loom::getFiberStatusName(f.status))});

        return fibers_info;
    }

}

